#pragma once
#include "socket_base.hpp"
#include <unistd.h>
#include <fcntl.h>
#include <cstring>
#include <system_error>

namespace uds_model {

// 尝试连接已存在的 Unix 域监听套接字，成功说明端口仍被占用
static bool still_alive(const std::string& path) {
    int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
    if (fd == -1) return false;
    sockaddr_un addr{};
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
    bool ok = (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == 0);
    close(fd);
    return ok;
}

// 独占式监听器：构造成功即获得所有权，失败抛 system_error
class exclusive_unix_listener {
public:
    explicit exclusive_unix_listener(const std::string& path)
        : path_(path), fd_(-1) {
        if (access(path.c_str(), F_OK) == 0) {
            if (still_alive(path))
                throw std::system_error(EADDRINUSE, std::system_category(),
                                        "unix socket in use: " + path);
            unlink(path.c_str());
        }
        fd_ = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (fd_ == -1)
            throw std::system_error(errno, std::system_category(), "socket");
        sockaddr_un addr{};
        addr.sun_family = AF_UNIX;
        strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path) - 1);
        if (bind(fd_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
            throw std::system_error(errno, std::system_category(), "bind");
        if (listen(fd_, SOMAXCONN) == -1)
            throw std::system_error(errno, std::system_category(), "listen");
    }
    ~exclusive_unix_listener() {
        if (fd_ != -1) {
            close(fd_);
            unlink(path_.c_str());
        }
    }
    int accept_nonblock() {
        int cfd = ::accept4(fd_, nullptr, nullptr,
                            SOCK_NONBLOCK | SOCK_CLOEXEC);
        if (cfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
            return -1;
        if (cfd == -1)
            throw std::system_error(errno, std::system_category(), "accept4");
        return cfd;
    }
    int native() const { return fd_; }
    const std::string& path() const { return path_; }
    exclusive_unix_listener(const exclusive_unix_listener&) = delete;
    exclusive_unix_listener& operator=(const exclusive_unix_listener&) = delete;
private:
    std::string path_;
    int         fd_;
};
}